const gps = (() => {
  const PI = 3.14159265358979324;
  const XPI = (PI * 3000.0) / 180.0;
  const A = 6378245.0;
  const EE = 0.00669342162296594323;
  const RC = 6378137; //赤道半径
  const RJ = 6356725; //极半径
  function transformLat(x, y) {
    return (
      -100.0 +
      2.0 * x +
      3.0 * y +
      0.2 * y * y +
      0.1 * x * y +
      0.2 * Math.sqrt(Math.abs(x)) +
      ((20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0) /
        3.0 +
      ((20.0 * Math.sin(y * PI) + 40.0 * Math.sin((y / 3.0) * PI)) * 2.0) /
        3.0 +
      ((160.0 * Math.sin((y / 12.0) * PI) + 320 * Math.sin((y * PI) / 30.0)) *
        2.0) /
        3.0
    );
  }
  function transformLng(x, y) {
    return (
      300.0 +
      x +
      2.0 * y +
      0.1 * x * x +
      0.1 * x * y +
      0.1 * Math.sqrt(Math.abs(x)) +
      ((20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0) /
        3.0 +
      ((20.0 * Math.sin(x * PI) + 40.0 * Math.sin((x / 3.0) * PI)) * 2.0) /
        3.0 +
      ((150.0 * Math.sin((x / 12.0) * PI) + 300.0 * Math.sin((x / 30.0) * PI)) *
        2.0) /
        3.0
    );
  }
  function outOfChina(lat, lng) {
    return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;
  }
  function transform(lat, lng) {
    let tLat = transformLat(lng - 105.0, lat - 35.0);
    let tLng = transformLng(lng - 105.0, lat - 35.0);
    let radLat = (lat / 180.0) * PI;
    let magic = Math.sin(radLat);
    magic = 1 - EE * magic * magic;
    let sqrtMagic = Math.sqrt(magic);
    tLat = (tLat * 180.0) / (((A * (1 - EE)) / (magic * sqrtMagic)) * PI);
    tLng = (tLng * 180.0) / ((A / sqrtMagic) * Math.cos(radLat) * PI);
    return {
      lat: tLat,
      lng: tLng
    };
  }
  function getRad(d) {
    return (d * PI) / 180.0;
  }
  function degreesToRadians(degrees) {
    const degToRadFactor = PI / 180;
    return degrees * degToRadFactor;
  }
  function LatLng(lat, lng) {
    this.lngDeg = parseInt(lng);
    this.lngMin = parseInt(((lng - this.lngDeg) * 60).toString());
    this.lngSec = (lng - this.lngDeg - this.lngMin / 60) * 3600;

    this.latDeg = parseInt(lat);
    this.latMin = parseInt(((lat - this.latDeg) * 60).toString());
    this.latSec = (lat - this.latDeg - this.latMin / 60) * 3600;

    this.lng = lng;
    this.lat = lat;

    this.lngRad = (lng * PI) / 180;
    this.latRad = (lat * PI) / 180;
    this.ec = RJ + ((RC - RJ) * (90 - lat)) / 90;
    this.ed = this.ec * Math.cos(this.latRad);
  }
  function getLatLng(latLng, distance, angle) {
    let dx = distance * 1000 * Math.sin(degreesToRadians(angle));
    let dy = distance * 1000 * Math.cos(degreesToRadians(angle));

    let bwd = ((dy / latLng.ec + latLng.latRad) * 180) / PI;
    let bjd = ((dx / latLng.ed + latLng.lngRad) * 180) / PI;
    return new LatLng(bwd, bjd);
  }
  return {
    wgs84ToGcj02: function(wgsLat, wgsLng) {
      if (outOfChina(wgsLat, wgsLng)) {
        return {
          lat: wgsLat,
          lng: wgsLng
        };
      }
      let t = transform(wgsLat, wgsLng);
      return {
        lat: wgsLat + t.lat,
        lng: wgsLng + t.lng
      };
    },
    gcj02ToWgs84: function(gcjLat, gcjLng) {
      if (outOfChina(gcjLat, gcjLng)) {
        return {
          lat: gcjLat,
          lng: gcjLng
        };
      }
      let t = transform(gcjLat, gcjLng);
      return {
        lat: gcjLat - t.lat,
        lng: gcjLng - t.lng
      };
    },
    gcj02ToBd09: function(gcjLat, gcjLng) {
      let z =
        Math.sqrt(gcjLng * gcjLng + gcjLat * gcjLat) +
        0.00002 * Math.sin(gcjLat * XPI);
      let theta =
        Math.atan2(gcjLat, gcjLng) + 0.000003 * Math.cos(gcjLng * XPI);
      let bdLng = z * Math.cos(theta) + 0.0065;
      let bdLat = z * Math.sin(theta) + 0.006;
      return {
        lat: bdLat,
        lng: bdLng
      };
    },
    bd09ToGcj02: function(bdLat, bdLng) {
      let x = bdLng - 0.0065,
        y = bdLat - 0.006;
      let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * XPI);
      let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * XPI);
      let gcjLng = z * Math.cos(theta);
      let gcjLat = z * Math.sin(theta);
      return {
        lat: gcjLat,
        lng: gcjLng
      };
    },
    wgs84ToBd09: function(wgsLat, wgsLng) {
      let gcj = this.wgs84ToGcj02(wgsLat, wgsLng);
      let bd09 = this.gcj02ToBd09(gcj.lat, gcj.lng);
      return {
        lat: bd09.lat,
        lng: bd09.lng
      };
    },
    bd09ToWgs84: function(bdLat, bdLng) {
      let gcj = this.bd09ToGcj02(bdLat, bdLng);
      let wgs = this.gcj02ToWgs84(gcj.lat, gcj.lng);
      return {
        lat: wgs.lat,
        lng: wgs.lng
      };
    },
    getFlatternDistance: function(lat1, lng1, lat2, lng2) {
      let f = getRad((lat1 + lat2) / 2);
      let g = getRad((lat1 - lat2) / 2);
      let l = getRad((lng1 - lng2) / 2);

      let sg = Math.sin(g);
      let sl = Math.sin(l);
      let sf = Math.sin(f);

      let s, c, w, r, d, h1, h2;
      let a = RC;
      let fl = 1 / 298.257;

      sg = sg * sg;
      sl = sl * sl;
      sf = sf * sf;

      s = sg * (1 - sl) + (1 - sf) * sl;
      c = (1 - sg) * (1 - sl) + sf * sl;

      w = Math.atan(Math.sqrt(s / c));
      r = Math.sqrt(s * c) / w;
      d = 2 * w * a;
      h1 = (3 * r - 1) / 2 / c;
      h2 = (3 * r + 1) / 2 / s;

      return d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg));
    },
    getCircleLatLng(lat, lng, distance) {
      let latLng = new LatLng(lat, lng);
      let array = [];
      let circleLatLng;
      for (let i = 0; i < 360; i++) {
        circleLatLng = getLatLng(latLng, distance, i);
        array[i] = [circleLatLng.lat, circleLatLng.lng];
      }
      return array;
    }
  };
})();

export default gps;
